home *** CD-ROM | disk | FTP | other *** search
-
- /* dispatch.c */
- /* Copyright (C) 1995 Gustav K. Larsson */
-
- /* ====================================================== */
-
- #define kMethodNotFound ((void *) 0)
- #define kClassNotFound ((void *) -1)
-
- typedef unsigned char uchar;
- typedef unsigned short ushort;
- typedef unsigned long ulong;
-
- typedef ushort ClassID;
- typedef ushort MethodNumber;
- typedef void * MethodAddress;
-
- typedef struct {
- MethodNumber methodNumber;
- MethodAddress methodAddress;
- } MethodEntry;
-
- typedef struct {
- ushort inheritedCount;
- ushort inheritedClasses[15];
- MethodNumber largestMethodNumber;
- ushort methodCount;
- MethodEntry methods[];
- } Class, *ClassPtr;
-
- /* ====================================================== */
-
- /*
- * Each block in the cache is 64 bytes and holds 10 entries.
- * The id field distinguishes class/method pairs that map to
- * the same cache block. The hits field has a bit for each
- * entry, which is set whenever there is a hit on the entry.
- * The "next" field is 0..9 and indicates where to check
- * first when deciding which entry to replace.
- */
-
- #define BLOCK_SIZE 10
-
- typedef struct {
- ushort id [ BLOCK_SIZE ];
- ushort hits;
- ushort next;
- MethodAddress methodAddress [ BLOCK_SIZE ];
- } CacheBlock;
-
- static CacheBlock Cache[256]; /* exactly 16K */
-
- /* ====================================================== */
-
- extern ClassPtr GetClassPtr( ClassID );
- MethodAddress FindMethod( ClassID, MethodNumber );
- static MethodAddress HandleMiss( ClassID, MethodNumber,
- CacheBlock *, ushort );
-
- /* ====================================================== */
-
- MethodAddress
- FindMethod( ClassID classID, MethodNumber methodNumber )
- {
- register ulong hash;
- register ushort *idPtr;
- register CacheBlock *block;
-
- /*
- * Check cache for this class & method.
- *
- * First, generate a hash key that is unique for each
- * class/method pair. The formula classID + 437 * method
- * works since 437 is greater than the highest class ID,
- * 400. The ideal multiplier depends heavily on the
- * distribution of class/method pairs, but 437 seems to
- * give a reasonable spread under a variety of conditions.
- *
- * The low byte of the hash key becomes the block number,
- * and the higher bytes become the "id" (to distinguish
- * class/method pairs that map to the same block). Add one
- * to the id so it is never zero (zero indicates an unused
- * cache entry).
- */
-
- hash = classID + 437L * methodNumber;
- block = &Cache[ (uchar)hash ];
- hash = (hash >> 8) + 1; /* now hash holds just the id */
-
- idPtr = block->id;
- if ( *idPtr++ == (ushort)hash ) goto hit0;
- if ( *idPtr++ == (ushort)hash ) goto hit1;
- if ( *idPtr++ == (ushort)hash ) goto hit2;
- if ( *idPtr++ == (ushort)hash ) goto hit3;
- if ( *idPtr++ == (ushort)hash ) goto hit4;
- if ( *idPtr++ == (ushort)hash ) goto hit5;
- if ( *idPtr++ == (ushort)hash ) goto hit6;
- if ( *idPtr++ == (ushort)hash ) goto hit7;
- if ( *idPtr++ == (ushort)hash ) goto hit8;
- if ( *idPtr++ == (ushort)hash ) goto hit9;
-
- return HandleMiss( classID, methodNumber,
- block, (ushort)hash );
-
- /* Handle cache hit */
-
- hit0: block->hits |= 0x001; return block->methodAddress[0];
- hit1: block->hits |= 0x002; return block->methodAddress[1];
- hit2: block->hits |= 0x004; return block->methodAddress[2];
- hit3: block->hits |= 0x008; return block->methodAddress[3];
- hit4: block->hits |= 0x010; return block->methodAddress[4];
- hit5: block->hits |= 0x020; return block->methodAddress[5];
- hit6: block->hits |= 0x040; return block->methodAddress[6];
- hit7: block->hits |= 0x080; return block->methodAddress[7];
- hit8: block->hits |= 0x100; return block->methodAddress[8];
- hit9: block->hits |= 0x200; return block->methodAddress[9];
-
- }
-
- /* ====================================================== */
-
- static MethodAddress
- HandleMiss( ClassID classID, MethodNumber methodNumber,
- CacheBlock *blockPtr, ushort id )
- {
- register ClassPtr classPtr;
- MethodAddress methodAddress;
- register ushort *temp; /* shared address register */
-
- /* Not in cache, so look it up the hard way */
-
- classPtr = GetClassPtr( classID );
- if ( classPtr != kClassNotFound ) {
-
- /* Look in this class. Use a binary search. */
-
- {
- register MethodEntry *methods = classPtr->methods;
- ushort searchSize = classPtr->methodCount;
- register MethodNumber methodReg = methodNumber;
- /* method # in a register */
-
- /*
- * Unroll the binary search for the most common cases.
- * The BINARY macro reduces the search to progressively
- * more elementary cases. If classPtr->methodCount is
- * greater than 32, then we run a general binary search
- * until the search is reduced to an unrolled case.
- */
-
- continueBinarySearch:
- switch ( searchSize )
- {
- bin2: case 2:
- if ( methods[1].methodNumber == methodReg ) {
- methodAddress = methods[1].methodAddress;
- goto addCache;
- }
- /* else fall through... */
- bin1: case 1:
- if ( methods[0].methodNumber == methodReg ) {
- methodAddress = methods[0].methodAddress;
- goto addCache;
- }
- else goto checkSuperclasses;
-
- #define BINARY(mid,mid1) \
- if ( methodReg < methods[mid].methodNumber ) \
- goto bin##mid; /* like "hi = mid" */ \
- else { \
- methods += mid; /* like "lo = mid" */ \
- goto bin##mid1; \
- }
-
- /* binN: case N: BINARY(N/2,(N+1)/2) */
- bin3: case 3: BINARY(1,2)
- bin4: case 4: BINARY(2,2)
- bin5: case 5: BINARY(2,3)
- bin6: case 6: BINARY(3,3)
- bin7: case 7: BINARY(3,4)
- bin8: case 8: BINARY(4,4)
- bin9: case 9: BINARY(4,5)
- bin10: case 10: BINARY(5,5)
- bin11: case 11: BINARY(5,6)
- bin12: case 12: BINARY(6,6)
- bin13: case 13: BINARY(6,7)
- bin14: case 14: BINARY(7,7)
- bin15: case 15: BINARY(7,8)
- bin16: case 16: BINARY(8,8)
- bin17: case 17: BINARY(8,9)
- bin18: case 18: BINARY(9,9)
- bin19: case 19: BINARY(9,10)
- bin20: case 20: BINARY(10,10)
- bin21: case 21: BINARY(10,11)
- bin22: case 22: BINARY(11,11)
- bin23: case 23: BINARY(11,12)
- bin24: case 24: BINARY(12,12)
- bin25: case 25: BINARY(12,13)
- bin26: case 26: BINARY(13,13)
- bin27: case 27: BINARY(13,14)
- bin28: case 28: BINARY(14,14)
- bin29: case 29: BINARY(14,15)
- bin30: case 30: BINARY(15,15)
- bin31: case 31: BINARY(15,16)
- bin32: case 32: BINARY(16,16)
-
- #define NUM_UNROLLED 32 /* # of unrolled cases */
-
- default:
- if (methodReg <= classPtr->largestMethodNumber) {
- register ushort lo, mid, hi, mn;
- lo = 0;
- hi = classPtr->methodCount - 1;
- while ( lo + NUM_UNROLLED - 1 < hi ) {
- mid = (lo + hi) >> 1;
- mn = methods[mid].methodNumber;
- if ( methodReg < mn )
- hi = mid;
- else if ( methodReg > mn )
- lo = mid;
- else {
- methodAddress = methods[mid].methodAddress;
- goto addCache;
- }
- }
- /* reduced to an unrolled case */
- searchSize = hi-lo+1;
- methods += lo;
- goto continueBinarySearch;
- }
- }
- }
-
- /*
- * Look in superclasses. Eliminating the recursion by
- * keeping a custom stack of critical variables doesn't
- * buy us much. Recursion also makes the code clearer.
- */
-
- checkSuperclasses:
- {
- register ulong i, max = classPtr->inheritedCount;
-
- /* shared address register points into class list */
- temp = classPtr->inheritedClasses;
-
- for ( i = 0; i < max; i++ ) {
- methodAddress = FindMethod(*temp++, methodNumber);
- if ( methodAddress != kMethodNotFound )
- goto addCache;
- }
- }
- }
-
- /*
- * There are two ways to get here: classPtr is
- * kClassNotFound (hopefully rare), or the method was not
- * found in any of the superclasses. Either way, put a
- * "not found" entry into the cache. This helps when
- * searching complicated inheritance hierarchies or
- * repeatedly looking up the same method in several
- * related subclasses.
- */
-
- methodAddress = kMethodNotFound;
-
- /*
- * Add an entry to the cache.
- */
-
- addCache:
-
- /* shared address register holds block pointer */
- temp = (ushort*) blockPtr;
- #define block ((CacheBlock*) temp)
-
- {
- register ulong hits = block->hits;
- register ulong next = block->next;
- register ulong mask = 1L << next;
-
- /*
- * Choose the entry to replace. Look for a cleared
- * bit in "hits" starting at "next". If all the bits
- * are 1 initially, go all the way around; we are
- * guaranteed to stop since we clear bits as we go.
- */
-
- while ( hits & mask ) {
- hits &= ~mask; /* no, clear the bit */
- mask <<= 1; /* and try next bit */
- if ( mask == (1 << BLOCK_SIZE) )
- mask = 1;
- next++;
- }
- if ( next >= BLOCK_SIZE )
- next -= BLOCK_SIZE;
-
- block->methodAddress[ next ] = methodAddress;
- block->id[ next ] = id;
- block->hits = hits;
- block->next = ( next < BLOCK_SIZE-1 ? next+1 : 0 );
- }
-
- return methodAddress;
- }
-